from cv2 import (
    Mat, cvtColor,
    COLOR_BGR2GRAY,
    imread, IMREAD_GRAYSCALE, matchTemplate, TM_CCOEFF_NORMED, minMaxLoc, IMREAD_GRAYSCALE, threshold, THRESH_BINARY, THRESH_OTSU, rectangle,imwrite,resize, erode,
    FONT_HERSHEY_SIMPLEX,putText,LINE_AA, IMREAD_COLOR, GaussianBlur, Canny, THRESH_BINARY_INV, getStructuringElement, MORPH_RECT, dilate, COLOR_BGR2HSV, inRange, findContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, drawContours, contourArea
)
import numpy as np
from skimage.metrics import structural_similarity as compare_ssim
# from concurrent.futures import ThreadPoolExecutor, Future
from extends.email import thread_pool_send_email
from typing import List, Optional, Dict
from get_logger import get_error_logger
from config import get_settings
from datetime import datetime
from PIL import Image

import pytesseract
import os
import re

error_logger = get_error_logger()
settings = get_settings()

class Mix:
    frame: Optional[Mat] = None
    previous: bool = True  # 上一次 不通过， True通过
    text = ""
    font = FONT_HERSHEY_SIMPLEX
    font_scale = 1  # 字体大小
    color = (0, 0, 255)  # 红色（BGR）
    thickness = 2  # 字体厚度

    def picture_frame(self, frame):
        position = (frame.shape[1] - settings.POSITION_X, settings.POSITION_Y)  # 右上角位置，可以调整

        # 在图像上绘制文本
        putText(frame, self.text, position, self.font, self.font_scale, self.color, self.thickness, lineType=LINE_AA)
    
    def save_debug_images(self, frame, name:str):
        debug_path = os.path.join(settings.BASE_DIR, "logs", "debug")
        if not os.path.exists(debug_path):
            os.makedirs(debug_path)
        imwrite(os.path.join(debug_path, name), frame)

    def start(self, frame, *args, **kwargs):
        raise NotImplementedError("子类必须实现此方法")

class CheckWindow(Mix):
    def __init__(self):

        self.MMI_WINDOW_SSIM_THRESHOLD = 0.83
        self.TOP_LEFT_CORNER_GRAYSCALE_TEMPLATE = imread(
            os.path.join(settings.BASE_DIR, "static", "top_left_corner_grayscale_template.png"), IMREAD_GRAYSCALE)
        self.TOP_RIGHT_CORNER_GRAYSCALE_TEMPLATE = imread(
            os.path.join(settings.BASE_DIR, "static", "top_right_corner_grayscale_template.png"), IMREAD_GRAYSCALE)
        self.BOTTOM_LEFT_CORNER_GRAYSCALE_TEMPLATE = imread(
            os.path.join(settings.BASE_DIR, "static", "bottom_left_corner_grayscale_template.png"), IMREAD_GRAYSCALE)
        self.BOTTOM_RIGHT_CORNER_GRAYSCALE_TEMPLATE = imread(
            os.path.join(settings.BASE_DIR, "static", "bottom_right_corner_grayscale_template.png"), IMREAD_GRAYSCALE)
        self.Top_Left_Corner = [[26, 51], [0, 28]],
        self.Top_Right_Corner = [[26, 51], [1892, 1920]],
        self.Bottom_Left_Corner = [[1024, 1049], [0, 28]],
        self.Bottom_Right_Corner = [[1024, 1049], [1892, 1920]],

        self.MMI_WINDOW_CORNER_AREA = [
            self.Top_Left_Corner[0],
            self.Top_Right_Corner[0],
            self.Bottom_Left_Corner[0],
            self.Bottom_Right_Corner[0],
        ]

        self.detect_mmi_window_futures: list[bool]
        self.text = "MMI not in full screen"

    def start(self, frame, *args, **kwargs):
        self.detect_mmi_window_futures = []
        gray_frame = kwargs.get("gray_frame")

        for index, coordinate in enumerate(self.MMI_WINDOW_CORNER_AREA):  # 屏幕的四个角区域
            start_x = coordinate[0][0]
            start_y = coordinate[0][1]
            end_x = coordinate[1][0]
            end_y = coordinate[1][1]
            self.detect_mmi_window_futures.append(
                self.detect_mmi_window(
                    index,
                    gray_frame[
                        start_x: start_y,
                        end_x: end_y
                    ]
                )
            )
        is_check_ok = all(self.detect_mmi_window_futures)
        
        self.record_log(is_check_ok, frame)
        return is_check_ok # 必须要全部为true才正常:返回true
    
    # def picture_frame(self, frame):
        # position = (frame.shape[1] - self.position_x, self.position_y)  # 右上角位置，可以调整

        # 在图像上绘制文本
        # putText(frame, self.text, position, self.font, self.font_scale, self.color, self.thickness, lineType=LINE_AA)


    def record_log(self, is_check_bool, frame):
        if is_check_bool == False:
            # 画图
            self.picture_frame(frame)

            # 截图和提示
            if  self.previous == True:
                # thread_pool_send_email(settings.TO_ADDR, "HHS", "检测到屏幕非全屏")
                error_logger.error("检测到屏幕非全屏")
                current_time = datetime.now()
                image_name = current_time.strftime("%Y-%m-%d %H:%M:%S") + f"window_ssim_error.jpg"
                imwrite(os.path.join(settings.BASE_DIR, "logs", "error_logger", image_name), frame)
        self.previous = is_check_bool


    def detect_mmi_window(self, corner_position, area):
        """检测屏幕是否是全屏
        """
        gray_corner_template: Mat = \
            self.TOP_LEFT_CORNER_GRAYSCALE_TEMPLATE  # 左上角灰色

        if corner_position == 1:
            gray_corner_template = \
                self.TOP_RIGHT_CORNER_GRAYSCALE_TEMPLATE  # 右上角

        elif corner_position == 2:
            gray_corner_template = \
                self.BOTTOM_LEFT_CORNER_GRAYSCALE_TEMPLATE  # 左下角

        elif corner_position == 3:
            gray_corner_template = \
                self.BOTTOM_RIGHT_CORNER_GRAYSCALE_TEMPLATE  # 右下角

        corner_mssim: float = compare_ssim(
            area,
            gray_corner_template,
            gaussian_weights=True,
            sigma=1.5,
            use_sample_covariance=False
        )

        if corner_mssim < self.MMI_WINDOW_SSIM_THRESHOLD:
            return False
        return True

class CheckLayout(Mix):
    """检测布局是否正确, 指定中间区域下面的那个灰色路段， 来进行对比"""
    def __init__(self):
        self.text = "Shifted / Changed depot layout."
        self.font = FONT_HERSHEY_SIMPLEX
        self.font_scale = 1
        self.color = (0, 0, 255)
        self.thickness = 2
        # self.position_x = 400
        # self.position_y = 150

        # 指定固定区域坐标
        self.ROI_X1 = 115  # 左上角x
        self.ROI_Y1 = 269  # 左上角y
        self.ROI_X2 = 165  # 右下角x
        self.ROI_Y2 = 314  # 右下角y 

        # 加载模板图片
        self.template = imread(
            os.path.join(settings.BASE_DIR, "static", "laout_image.png"),
            IMREAD_GRAYSCALE
        )
        if self.template is None:
            raise FileNotFoundError("无法加载布局模板图片")
            
        
        debug_path = os.path.join(settings.BASE_DIR, "logs", "debug")
        if not os.path.exists(debug_path):
            os.makedirs(debug_path)

        # 获取模板图片ROI区域并预处理
        template_roi = self.template[self.ROI_Y1:self.ROI_Y2, self.ROI_X1:self.ROI_X2]
        self.template_processed = self._preprocess_image(template_roi)
        imwrite(os.path.join(debug_path, "template_roi_processed.png"), self.template_processed)

        self.previous = True
        self.LAYOUT_SIMILARITY_THRESHOLD = 0.90

    def _preprocess_image(self, gray):
        """预处理图片，忽略颜色变化"""
        # 转换为灰度图
        # gray = cvtColor(image, COLOR_BGR2GRAY)
        # 高斯模糊去噪
        blurred = GaussianBlur(gray, (5, 5), 0)
        # Canny边缘检测
        edges = Canny(blurred, 50, 150)
        # 二值化
        _, binary = threshold(edges, 127, 255, THRESH_BINARY)
        return binary

    def start(self, frame, *args, **kwargs):
        gray_frame = kwargs.get("gray_frame")
        gray_roi = gray_frame[self.ROI_Y1:self.ROI_Y2, self.ROI_X1:self.ROI_X2]
        # 预处理当前帧
        current_processed = self._preprocess_image(gray_roi)
        
        # 计算结构相似度
        score = compare_ssim(
            current_processed,
            self.template_processed,
            full=True)[0]  # 只取相似度值
        
        # 判断布局是否正确
        is_correct_layout = score >= self.LAYOUT_SIMILARITY_THRESHOLD
        
        # 记录结果
        self.record_layout_check(is_correct_layout, frame)
        
        return is_correct_layout

    def record_layout_check(self, is_correct, frame):
        if not is_correct:
            self.picture_frame(frame)
            if self.previous:
                error_logger.error("检测到布局异常")
                current_time = datetime.now()
                image_name = current_time.strftime("%Y-%m-%d_%H-%M-%S") + "_wrong_layout.jpg"
                imwrite(os.path.join(settings.BASE_DIR, "logs", "error_images", image_name), frame)
        
        self.previous = is_correct

class CheckFreezeMix(Mix):
    def __init__(self):
        self.text = "Frozen Screen Scenario"
        self.font = FONT_HERSHEY_SIMPLEX
        self.font_scale = 1
        self.color = (0, 0, 255)
        self.thickness = 2
        # self.position_x = 400
        # self.position_y = 200
        
        self.previous_frame = None
        self.last_change_time = datetime.now()
        self.previous = True

        # 解析高峰期时间
        peak_start = datetime.strptime(settings.PEAK_START_TIME, "%H:%M").time()
        peak_end = datetime.strptime(settings.PEAK_END_TIME, "%H:%M").time()
        self.peak_period = (peak_start, peak_end)

    def _is_peak_time(self) -> bool:
        """判断当前是否处于高峰期"""
        current_time = datetime.now().time()
        peak_start, peak_end = self.peak_period

        if peak_start < peak_end:
            return peak_start <= current_time <= peak_end
        else:
            return current_time >= peak_start or current_time <= peak_end


    def _get_freeze_threshold(self) -> int:
        """根据时段返回冻结阈值(秒)"""
        return (settings.PEAK_FREEZE_THRESHOLD if self._is_peak_time() 
                else settings.NORMAL_FREEZE_THRESHOLD) * 60

class CheckFreeze2(CheckFreezeMix):
    """改为识别绿色灯和路段点, 是否发生变化来判断是否冻结"""

    def __init__(self):
        super().__init__()
        # 上次识别到的轮廓
        self.previous_green_single_contours = None # 上次识别到的绿色灯
        self.previous_green_point_contours = None # 上次识别到的路段点

    def start(self, frame, *args, **kwargs):
        green_signle_contours = kwargs.get("green_signle_contours")
        green_point_contours = kwargs.get("green_point_contours")

        # 比较当前的 green_single_contours 与上次的是否相同
        if self.previous_green_single_contours is not None:
            same_single = (len(green_signle_contours) == len(self.previous_green_single_contours) and
                           all(np.array_equal(c1, c2) for c1, c2 in zip(green_signle_contours, self.previous_green_single_contours)))
        else:
            same_single = False

        # 比较当前的 green_point_contours 与上次的是否相同
        if self.previous_green_point_contours is not None:
            same_point = (len(green_point_contours) == len(self.previous_green_point_contours) and
                          all(np.array_equal(c1, c2) for c1, c2 in zip(green_point_contours, self.previous_green_point_contours)))
        else:
            same_point = False

        # 如果两个轮廓都没有变化，认为画面冻结
        if same_single and same_point:
            freeze_duration = (datetime.now() - self.last_change_time).total_seconds()
            freeze_threshold = self._get_freeze_threshold()
            is_not_frozen = freeze_duration < freeze_threshold
        else:
            self.last_change_time = datetime.now()
            is_not_frozen = True

        # 记录结果
        if not is_not_frozen:
            self.picture_frame(frame)
            if self.previous:
                error_logger.error(
                    f"检测到画面冻结 {freeze_duration/60:.1f} 分钟"
                    f"({'高峰期' if self._is_peak_time() else '非高峰期'})"
                )

        self.previous = is_not_frozen
        self.previous_green_single_contours = green_signle_contours
        self.previous_green_point_contours = green_point_contours

        return is_not_frozen



class CheckFreeze(CheckFreezeMix):
    """使用全屏图像对比检测屏幕是否冻结"""
    def __init__(self):
        super().__init__()
        
        # 相似度阈值(如果这个发生小于了0.997证明发生了变化, 设置越大页面变化记录越敏感)
        self.FRAME_SIMILARITY_THRESHOLD = 0.9965

    def start(self, frame, *args, **kwargs):
        gray_frame = kwargs.get("gray_frame")
        if self.previous_frame is None:
            self.previous_frame = gray_frame
            return True
            
        # 转换当前帧为灰度图
        current_frame = gray_frame
        
        # 计算与上一帧的相似度
        score = compare_ssim(
            current_frame,
            self.previous_frame,
            full=True
        )[0]
        
        # 如果画面变化明显，更新时间和参考帧
        if score < self.FRAME_SIMILARITY_THRESHOLD:
            self.last_change_time = datetime.now()
            self.previous_frame = current_frame
            self.previous = True
            return True
            
        # 计算冻结时间
        freeze_duration = (datetime.now() - self.last_change_time).total_seconds()
        freeze_threshold = self._get_freeze_threshold()
        
        # 判断是否超过阈值
        is_not_frozen = freeze_duration < freeze_threshold
        
        # 记录结果
        if not is_not_frozen:
            self.picture_frame(frame)
            if self.previous:
                error_logger.error(
                    f"检测到画面冻结 {freeze_duration/60:.1f} 分钟"
                    f"({'高峰期' if self._is_peak_time() else '非高峰期'})"
                )
                
        self.previous = is_not_frozen
        return is_not_frozen


class DatetimeOcr(Mix):
    def __init__(self) -> None:
        self.x1, self.y1, self.x2, self.y2 = 1554, 20, 1852, 45 # 左上， 右下
        pytesseract.pytesseract.tesseract_cmd = '/usr/bin/tesseract' # 需要修改成ocr安装地址, 当前使用的是linux默认
        self.date_format = "%m/%d/%y %I:%M:%S %p"
        self.custom_config = r'--psm 6 -c tessedit_char_whitelist="APM0123456789:/ "' # 限制识别到的字符
        self.pattern = r'\d{2}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} [APM]{2}' # 识别的时间格式
        self.timestamp = 0
        # self.format_regex = re.compile(r"(?P<m>1[0-2]|0[1-9]|[1-9])/(?P<d>[0-2][0-9]|3[0-1]|\d)/(?P<y>\d{2}) (?P<I>1[0-2]|0[1-9]|[1-9]):(?P<M>[0-5]\d|\d):(?P<S>6[0-1]|[0-5]\d|\d) (?P<p>AM|PM)")

    def format_datetime(self, datetime_text) -> str:
        return datetime.strptime(datetime_text, self.date_format)

    def calculate_timestamp(self, datetime_text) -> Optional[float]:
        try:
            match = re.match(self.pattern, datetime_text)
            cleaned_time = match.group()
            date_object = self.format_datetime(cleaned_time)
            current_time = datetime.now()
        except Exception as e:
            # 识别失败
            if self.timestamp and self.previous == False:
                error_logger.error(f"时间[{datetime_text}]解析失败: {e}")
            self.previous = True
            self.timestamp = -999999
        else:
            date_object = date_object.replace(year=current_time.year, month=current_time.month, day=current_time.day, microsecond=current_time.microsecond)
            time_difference = current_time - date_object
            self.timestamp = time_difference.total_seconds()
            self.previous = False
        return self.timestamp

    def start(self, frame, *args, **kwargs):
        gray_frame = kwargs.get("gray_frame")
        gray_roi = gray_frame[self.y1:self.y2, self.x1:self.x2]

        # gray_roi = cvtColor(roi, COLOR_BGR2GRAY)
        _, binary_roi = threshold(gray_roi, 0, 255, THRESH_BINARY | THRESH_OTSU)
        pil_image = Image.fromarray(binary_roi)
        
        datetime_text = self.get_datetime(pil_image)
        return self.calculate_timestamp(datetime_text)

    def get_datetime(self, frame):
        return pytesseract.image_to_string(frame, config=self.custom_config)
        

class CheckCursor(Mix):
    def __init__(self):
        self.text = "Cursor detected in depot layout."
        self.font = FONT_HERSHEY_SIMPLEX
        self.font_scale = 1
        self.color = (0, 0, 255)
        self.thickness = 2
        self.edges_roi = None
        # self.position_x = 400
        # self.position_y = 200

        # 定义黄色的HSV范围
        self.lower_yellow = np.array([20, 100, 100])
        self.upper_yellow = np.array([30, 255, 255])


        # 白色鼠标颜色
        self.lower_cursor_white = np.array([0, 0, 250])
        self.upper_cursor_white = np.array([90, 5, 255])

        # 鼠标颜色
        self.template_file_dict = {
            "yellow": "yellow_mouse_template.png",
            "black": "black_mouse_template.png",
            "white": "white_mouse_template.png",
            "test": "test_mouse_template.png"
        }
        self.mouse_color = settings.MOUSE_COLOR

        # 读取鼠标
        self.mouse_template, self.edges_template = self.mouse_template_init()

        self.save_debug_images(self.mouse_template, "debug_mouse_template.png")
        self.save_debug_images(self.edges_template, "debug_edges_template.png")

        # 指定检测区域坐标
        self.ROI_X1 = 43  # 左上角x
        self.ROI_Y1 = 258  # 左上角y
        self.ROI_X2 = 1788  # 右下角x
        self.ROI_Y2 = 959  # 右下角y
    
    def mouse_template_init(self):
        if self.mouse_color == "yellow":
            return self.yellow_mouse_template_init()
        elif self.mouse_color == "black":
            return self.black_mouse_template_init()
        else:
            return self.white_mouse_template_init()
    
    def yellow_mouse_template_init(self):
        # 加载鼠标模板图片
        mouse_template = imread(
            os.path.join(settings.BASE_DIR, "static", self.template_file_dict[self.mouse_color]),
            IMREAD_COLOR
        )
        if mouse_template is None:
            raise FileNotFoundError("无法加载鼠标模板图片")
        
        hsv_template = cvtColor(mouse_template, COLOR_BGR2HSV)
        # 过滤出黄色部分
        edges_template = inRange(hsv_template, self.lower_yellow, self.upper_yellow)
        return mouse_template, edges_template
    
    def black_mouse_template_init(self):
        mouse_template = imread(
            os.path.join(settings.BASE_DIR, "static", self.template_file_dict[self.mouse_color]),
            IMREAD_GRAYSCALE
        )
        if mouse_template is None:
            raise FileNotFoundError("无法加载鼠标模板图片")
        
        _, edges_template = threshold(mouse_template, 50, 255, THRESH_BINARY_INV)

        return mouse_template, edges_template
    
    def white_mouse_template_init(self):
        # 加载鼠标模板图片
        mouse_template = imread(
            os.path.join(settings.BASE_DIR, "static", self.template_file_dict[self.mouse_color]),
            IMREAD_COLOR
        )
        if mouse_template is None:
            raise FileNotFoundError("无法加载鼠标模板图片")

        hsv_template = cvtColor(mouse_template, COLOR_BGR2HSV)
        # 过滤出白色部分
        lower_white = np.array([0, 0, 200])
        upper_white = np.array([180, 20, 255])
        edges_template = inRange(hsv_template, lower_white, upper_white)
        # kernel = getStructuringElement(MORPH_RECT, (3, 3))
        # edges_template = erode(edges_template, kernel, iterations=1)
        # edges_template = dilate(edges_template, kernel, iterations=1)
        return mouse_template, edges_template

    def white_mouse_process(self, roi):
        hsv_roi = cvtColor(roi, COLOR_BGR2HSV)
        # lower_white = np.array([0, 0, 240])
        # upper_white = np.array([10, 15, 255])
        edges_roi = inRange(hsv_roi, self.lower_cursor_white, self.upper_cursor_white)
        self.save_debug_images(edges_roi, "debug_white_mouse_roi_1.png")
        self.edges_roi = edges_roi

        # 形态学操作去除小面积杂乱元素
        kernel = getStructuringElement(MORPH_RECT, (1, 2))
        temp_edges_roi = erode(edges_roi, kernel, iterations=1)
        temp_edges_roi = dilate(temp_edges_roi, kernel, iterations=1)
        self.save_debug_images(temp_edges_roi, "debug_white_mouse_roi_2.png")

        # 轮廓检测
        contours, _ = findContours(temp_edges_roi, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE)

        # 找到鼠标大小的轮廓
        cursor_contours = []
        for contour in contours:
            area = contourArea(contour)
            if 90 < area < 200:
                cursor_contours.append(contour)

        # filtered_contours = [contour for contour in contours if any(np.array_equal(contour, max_contour) for max_contour in cursor_contours)]
        # # 绘制找到的轮廓
        # for contour in filtered_contours:
            # drawContours(roi, [contour], -1, (0, 255, 0), 2)  # 绿色轮廓

        # 创建一个空白图像用于绘制轮廓
        mask = np.zeros_like(temp_edges_roi)
        for contour in cursor_contours:
            drawContours(mask, [contour], -1, 255, thickness=-1)  # 填充轮廓
        return mask
    
    def yellow_mouse_process(self, roi) -> Mat:
        hsv_roi = cvtColor(roi, COLOR_BGR2HSV)
        edges_roi = inRange(hsv_roi, self.lower_yellow, self.upper_yellow)
        return edges_roi
    
    def black_mouse_process(self, gray_roi):
        # gray_roi = cvtColor(roi, COLOR_BGR2GRAY)

        # 预处理图像
        _, edges_roi = threshold(gray_roi, 50, 255, THRESH_BINARY_INV)
    
        # 形态学操作去除小面积杂乱元素
        kernel = getStructuringElement(MORPH_RECT, (3, 3))
        edges_roi = erode(edges_roi, kernel, iterations=1)
        edges_roi = dilate(edges_roi, kernel, iterations=1)
        return edges_roi
    
    def mouse_process(self, frame, *args, **kwargs):

        if self.mouse_color == "yellow":
            roi = frame[self.ROI_Y1:self.ROI_Y2, self.ROI_X1:self.ROI_X2]
            return self.yellow_mouse_process(roi)
        elif self.mouse_color == "black":
            gray_frame = kwargs.get("gray_frame")
            gray_roi = gray_frame[self.ROI_Y1:self.ROI_Y2, self.ROI_X1:self.ROI_X2]
            return self.black_mouse_process(gray_roi)
        else:
            roi = frame[self.ROI_Y1:self.ROI_Y2, self.ROI_X1:self.ROI_X2]
            return self.white_mouse_process(roi)

    def start(self, frame, *args, **kwargs):
        # 提取检测区域
        edges_roi = self.mouse_process(frame, *args, **kwargs)
        self.save_debug_images(edges_roi, "debug_mouse_roi.png")

        # 模板匹配
        result = matchTemplate(edges_roi, self.edges_template, TM_CCOEFF_NORMED)
        min_val, max_val, min_loc, max_loc = minMaxLoc(result)

        threshold_max = 0.77

        # 如果匹配值低于阈值，重新匹配原始的图像
        if self.edges_roi and  max_val <= threshold_max:
            result = matchTemplate(self.edges_roi, self.edges_template, TM_CCOEFF_NORMED)
            min_val, max_val, min_loc, max_loc = minMaxLoc(result)

        # 如果匹配值超过阈值，认为检测到鼠标
        if max_val >= threshold_max:
            top_left = (max_loc[0] + self.ROI_X1, max_loc[1] + self.ROI_Y1)
            bottom_right = (top_left[0] + self.mouse_template.shape[1], top_left[1] + self.mouse_template.shape[0])
            rectangle(frame, top_left, bottom_right, (0, 0, 255), 2)
            self.picture_frame(frame)
            return False  # 检测到鼠标，返回False

        return True  # 未检测到鼠标，返回True

    # def picture_frame(self, frame):
        # position = (frame.shape[1] - self.position_x, self.position_y)
        # putText(frame, self.text, position, self.font, self.font_scale, self.color, self.thickness, lineType=LINE_AA)


class CheckVideo:
    def __init__(self):
        self.checkers: Dict[str, Mix] = {}
        self.previous_checker_text = ""
        # self.results = {}
        # self.check_thread_pool_list: List[Future] = []
        # self.check_thread_pool: ThreadPoolExecutor = ThreadPoolExecutor(max_workers=3)

        if settings.ENABLED_CHECK_WINDOWS:
            self.add_checker("check_windows", CheckWindow())

        if settings.ENABLED_CHECK_LAYOUT:  
            self.add_checker("check_layout", CheckLayout())
        
        if settings.ENABLED_CHECK_FREEZE:
            self.add_checker("check_freeze", CheckFreeze2())

        if settings.ENABLED_CHECK_CURSOR:
            self.add_checker("check_cursor", CheckCursor())

        if settings.ENABLED_TATE_TIME_OCR:
            self.add_checker("datetime_ocr", DatetimeOcr())

    def add_checker(self, name, checker):
        self.checkers[name] = checker
        # self.results[name] = None

    def start_checkers(self, frame, *args, **kwargs):
        """同步版本启动检测
        """
        results: Dict[str, str] = {
            "remark": ""
        }
        gray_frame = cvtColor(frame, COLOR_BGR2GRAY) # 灰度

        for name, checker in self.checkers.items():
            if hasattr(checker, "start"):
                check_results = checker.start(frame, *args, **kwargs, gray_frame=gray_frame)
                results[name] = check_results
                results["remark"] = checker.text
                if not check_results:
                    if settings.ENABLED_EMAIL and checker.text != self.previous_checker_text:
                        thread_pool_send_email(settings.TO_ADDR, "Smart Watch Dog TKD", f"{checker.text}")
                        self.previous_checker_text = checker.text
                    # 如果有其中一个为False则为检测不通过, 直接退出
                    break
        if all(results.values()):
            self.previous_checker_text = ""
        return results

    # def thread_start_checkers(self, frame):
    #     """使用多线程启动检测
    #     """
    #     # 初始化一个字典来存储Future和其对应检查器名字的关系
    #     future_to_name = {}
    #     self.check_thread_pool_list = []
    #     results = {}

    #     for name, checker in self.checkers.items():
    #         if hasattr(checker, "start"):
    #             future = self.check_thread_pool.submit(checker.start, frame)
    #             future_to_name[future] = name  # 记录Future与检查器名字的对应关系
    #             self.check_thread_pool_list.append(future)

    #     # 等待所有Future完成并收集结果
    #     for future in concurrent.futures.as_completed(self.check_thread_pool_list):
    #         name = future_to_name[future]  # 根据Future找到对应的检查器名字
    #         try:
    #             results[name] = future.result()  # 正确地存储每个检查器的结果
    #         except Exception as exc:
    #             error_logger.error(f"Checker {name} failed with error: {exc}")
    #             results[name] = None  # 或者根据需要处理异常情况
    #     return results

    # def close(self):
    #     if self.check_thread_pool:
    #         self.check_thread_pool.shutdown()


